// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.neuronbit.businessflow.intellij.ui.projectView;

import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilCore;
import com.neuronbit.businessflow.intellij.index.FlowClassIndex;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

/**
 * The type Flow.
 */
public class Flow implements Navigatable, Iterable<PsiElement> {
    /**
     * The constant DATA_KEY.
     */
    public static final DataKey<Flow[]> DATA_KEY = DataKey.create("form.array");

    private final Collection<PsiFile> myFormFiles;
    @NotNull
    private final PsiClass myClassToBind;

    /**
     * Instantiates a new Flow.
     *
     * @param classToBind the class to bind
     */
    public Flow(@NotNull PsiClass classToBind) {
        myClassToBind = classToBind;
        myFormFiles = FlowClassIndex.findFormsBoundToClass(classToBind.getProject(), classToBind);
    }

    /**
     * Instantiates a new Flow.
     *
     * @param classToBind the class to bind
     * @param formFiles   the form files
     */
    public Flow(@NotNull PsiClass classToBind, Collection<? extends PsiFile> formFiles) {
        myClassToBind = classToBind;
        myFormFiles = new HashSet<>(formFiles);
    }

    public boolean equals(Object object) {
        if (object instanceof Flow flow){
            return myFormFiles.equals(flow.myFormFiles) && myClassToBind.equals(flow.myClassToBind);
        } else {
            return false;
        }
    }

    public int hashCode() {
        return myFormFiles.hashCode() ^ myClassToBind.hashCode();
    }

    /**
     * Gets name.
     *
     * @return the name
     */
    public String getName() {
        return myClassToBind.getName();
    }

    /**
     * Gets class to bind.
     *
     * @return the class to bind
     */
    @NotNull
    public PsiClass getClassToBind() {
        return myClassToBind;
    }

    /**
     * Get form files psi file [ ].
     *
     * @return the psi file [ ]
     */
    public PsiFile[] getFormFiles() {
        return PsiUtilCore.toPsiFileArray(myFormFiles);
    }

    @Override
    public void navigate(boolean requestFocus) {
        for (PsiFile psiFile : myFormFiles) {
            if (psiFile != null && psiFile.canNavigate()) {
                psiFile.navigate(requestFocus);
            }
        }
    }

    @Override
    public boolean canNavigateToSource() {
        for (PsiFile psiFile : myFormFiles) {
            if (psiFile != null && psiFile.canNavigateToSource()) return true;
        }
        return false;
    }

    @Override
    public boolean canNavigate() {
        for (PsiFile psiFile : myFormFiles) {
            if (psiFile != null && psiFile.canNavigate()) return true;
        }
        return false;
    }

    /**
     * Is valid boolean.
     *
     * @return the boolean
     */
    public boolean isValid() {
        if (myFormFiles.size() == 0) return false;
        for (PsiFile psiFile : myFormFiles) {
            if (!psiFile.isValid()) {
                return false;
            }
        }
        if (!myClassToBind.isValid()) {
            return false;
        }
        return true;
    }

    /**
     * Contains file boolean.
     *
     * @param vFile the v file
     * @return the boolean
     */
    public boolean containsFile(final VirtualFile vFile) {
        final PsiFile classFile = myClassToBind.getContainingFile();
        final VirtualFile classVFile = classFile == null ? null : classFile.getVirtualFile();
        if (classVFile != null && classVFile.equals(vFile)) {
            return true;
        }
        for (PsiFile psiFile : myFormFiles) {
            final VirtualFile virtualFile = psiFile.getVirtualFile();
            if (virtualFile != null && virtualFile.equals(vFile)) {
                return true;
            }
        }
        return false;
    }

    @NotNull
    @Override
    public Iterator<PsiElement> iterator() {
        ArrayList<PsiElement> list = new ArrayList<>(myFormFiles);
        list.add(0, myClassToBind);
        return list.iterator();
    }
}
